#pragma once

#ifndef _VECTOR2_H_
#define _VECTOR2_H_

template<typename T> struct Vector2_T;
template<typename T> struct Vec2Constants
{
	static Vector2_T<T> fVec2_Zero;
	static Vector2_T<T> fVec2_OneX;
	static Vector2_T<T> fVec2_OneY;
	static Vector2_T<T> fVec2_OneZ;
	static Vector2_T<T> fVec2_One;
};

template<class F>
class Vector2_T
{
public:
	T x,y;

#ifdef _DEBUG
	ILINE Vector2_T() 
	{
		if (sizeof(F)==4)
		{
			uint32* p=(uint32*)&x;
			p[0]=F32SNAN;	p[1]=F32SNAN;
		}
		if (sizeof(F)==8)
		{
			uint64* p=(uint64*)&x;
			p[0]=F64SNAN;	p[1]=F64SNAN;
		}
	}
#else
	ILINE Vector2_T()	{};
#endif

	Vector2_T(type_zero) : x(0),y(0) {}

	ILINE Vector2_T(F vx,F vy) { x=F(vx); y=F(vy);  }

	ILINE Vector2_T& set(F nx,F ny) { x=F(nx); y=F(ny); return *this; }

	template<class F1> ILINE Vector2_T(const Vector2_T<F1> &src) { x=F(src.x); y=F(src.y); }
	template<class F1> ILINE explicit Vector2_T(const Vector3_T<F1> &src) { x=F(src.x); y=F(src.y); }
	template<class F1> ILINE explicit Vector2_T(const F1 *psrc) { x=F(psrc[0]); y=F(psrc[1]); }

	int operator!() const { return x==0 && y==0; }

	// The default Normalize function is in fact "safe". 0 vectors remain unchanged.
	Vector2_T& Normalize() 
	{ 
		F fInvLen = isqrt_safe_T(x*x+y*y);
		x *= fInvLen; y *= fInvLen; 
		return *this; 
	}

	// Normalize if non-0, otherwise set to specified "safe" value.
	Vector2_T& NormalizeSafe( const struct Vec2_T<F>& safe = Vec2Constants<F>::fVec2_Zero ) 
	{ 
		F fLen2 = x*x+y*y;
		if (fLen2 > 0.0f)
		{
			F fInvLen = isqrt_T(fLen2);
			x *= fInvLen; y *= fInvLen;
		}
		else
		{
			*this = safe;
		}
		return *this; 
	}

	Vector2_T GetNormalized() const 
	{ 
		F fInvLen = isqrt_safe_T(x*x+y*y);
		return *this * fInvLen;
	}

	Vector2_T GetNormalizedSafe( const struct Vector2_T<F>& safe = Vec2Constants<F>::fVec2_OneX ) const 
	{ 
		F fLen2 = x*x+y*y;	
		if (fLen2 > 0.0f)
		{
			F fInvLen = isqrt_T(fLen2);
			return *this * fInvLen;
		}
		else
		{
			return safe;
		}
	}

	F GetLength() const { return sqrt_T(x*x+y*y); }
	F GetLength2() const { return x*x+y*y; }

	void SetLength(F fLen)
	{ 
		F fLenMe = GetLength2();
		if(fLenMe<0.00001f*0.00001f)
			return;
		fLenMe = fLen * isqrt_T(fLenMe);
		x*=fLenMe; y*=fLenMe;
	}

	F area() const { return x*y; }

	F& operator[](int idx) { return *((F*)&x+idx); }
	F operator[](int idx) const { return *((F*)&x+idx); }
	operator F*() { return &x; }
	Vector2_T& flip() { x=-x;y=-y; return *this; }
	Vector2_T& zero() { x=y=0; return *this; }
	Vector2_T rot90ccw() { return Vector2_T(-y,x); }
	Vector2_T rot90cw() { return Vector2_T(y,-x); }

	F atan2() const { return atan2_T(y,x); }

	Vector2_T operator-() const { return Vector2_T(-x,-y); }

	Vector2_T operator*(F k) const { return Vector2_T(x*k,y*k); }
	Vector2_T& operator*=(F k) { x*=k;y*=k; return *this; }
	Vector2_T operator/(F k) const { return *this*((F)1.0/k); }
	Vector2_T& operator/=(F k) { return *this*=((F)1.0/k); }

	bool operator!=(const Vec2_T<F> &vec) { return !(*this == vec); }

	friend bool operator==(const Vec2_T<F> &left, const Vec2_T<F> &right) { return left.x == right.x && left.y == right.y; }
	friend bool operator!=(const Vec2_T<F> &left, const Vec2_T<F> &right)	{	return !(left==right);	}

	ILINE bool IsZero(F e=(F)0.0) const
	{
		return  (fabs_T(x)<=e) && (fabs_T(y)<=e);
	}

	ILINE bool IsZeroFast(F e=(F)0.0003) const
	{
		return (fabs_T(x) + fabs_T(y))<e;
	}
	F Dot(const Vector2_T &rhs) const {return x*rhs.x + y*rhs.y;}
	/// returns a vector perpendicular to this one (this->Cross(newVec) points "up")
	Vector2_T Perp() const {return Vector2_T(-y, x);}

	// The size of the "paralell-trapets" area spanned by the two vectors.
	ILINE F Cross (const Vector2_T<F> &v) const
	{
		return float (x*v.y - y*v.x);
	}	

	bool IsValid() const
	{
// 		if (!NumberValid(x)) return false;
// 		if (!NumberValid(y)) return false;
		return true;
	}
};

#endif//_VECTOR2_H_